﻿using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEditor.Experimental.GraphView;
using UnityEngine;
using UnityEngine.UIElements;

namespace SkillEditor
{
    public class SkillEditorSpaceWindow : EditorWindow
    {
        public SkillEditorGraph graph;

        public GraphSubWindow subWindow;       

        public SkillContentData data;
        public int skillSpaceIndex;
        public string[] skillSpaceIds;       

        public string[] triggerLabels;
        public int[] triggers;

        public Dictionary<string, SkillEditorNodeInfo> dicNodeInfo;

        private SkillSpace space => data.spaces[skillSpaceIndex];
        private SkillEditorSearchWindowProvider searchWindowProvider;

        public void Init()
        {
            SkillEditorConfig config = AssetDatabase.LoadAssetAtPath<SkillEditorConfig>("Assets/SkillEditor/Editor/SkillEditorConfig.asset");

            dicNodeInfo = new Dictionary<string, SkillEditorNodeInfo>();
            var arrInfos = Directory.GetFiles("Assets/SkillEditor/Node");

            for (int i = 0; i < arrInfos.Length; i++)
            {
                if (arrInfos[i].EndsWith("meta"))
                    continue;
                var info = AssetDatabase.LoadAssetAtPath<SkillEditorNodeInfo>(arrInfos[i]);
                if (info != null)
                {
                    dicNodeInfo.Add(info.id, info);
                }
            }

            triggers = new int[dicTriggers.Count];
            triggerLabels = new string[dicTriggers.Count];
            int index = 0;
            foreach (var item in dicTriggers)
            {
                triggers[index] = item.Key;
                triggerLabels[index] = item.Key + "." + item.Value;
                index++;
            }

            subWindow = new GraphSubWindow();
            rootVisualElement.Add(subWindow);
            subWindow.visible = false;

            rootVisualElement.RegisterCallback<ChangeDataEvent>(ChangeDataHandler, TrickleDown.NoTrickleDown);

            searchWindowProvider = new SkillEditorSearchWindowProvider();
            searchWindowProvider.Initialize(this, CreateNewNode);

            var toolbar = new IMGUIContainer(() =>
            {
                GUILayout.BeginHorizontal(EditorStyles.toolbar);

                if (data != null)
                {
                    if (GUILayout.Button("Save", EditorStyles.toolbarButton))
                    {
                        FileSave();
                    }

                    GUILayout.FlexibleSpace();
                    GUILayout.Label("Space:");
                    int tempSkillSpaceIndex = EditorGUILayout.Popup(skillSpaceIndex, skillSpaceIds, GUILayout.Width(120f));
                    if (tempSkillSpaceIndex != skillSpaceIndex)
                    {
                        skillSpaceIndex = tempSkillSpaceIndex;
                        Refresh();
                    }
                    GUILayout.Space(4);

                    GUILayout.Label("Trigger:");
                    space.trigger = EditorGUILayout.IntPopup(space.trigger, triggerLabels, triggers);

                    GUILayout.Space(4);
                    GUILayout.Label("Global:");
                    space.global = GUILayout.Toggle(space.global == 1, "") ? 1 : 0;

                    GUILayout.Space(4);

                    if (GUILayout.Button("New Space", EditorStyles.miniButton))
                    {
                        List<SkillSpace> list = new List<SkillSpace>(data.spaces);
                        list.Add(new SkillSpace());
                        list[list.Count - 1].id = "Space" + list.Count;
                        data.spaces = list.ToArray();
                        skillSpaceIndex = list.Count - 1;
                        Refresh();
                    }
                    GUILayout.Space(4);
                    if (GUILayout.Button("Del Space", EditorStyles.miniButton))
                    {
                        List<SkillSpace> list = new List<SkillSpace>(data.spaces);
                        list.RemoveAt(skillSpaceIndex);
                        data.spaces = list.ToArray();
                        skillSpaceIndex = 0;
                        Refresh(); ;
                    }
                }

                GUILayout.EndHorizontal();
            });
            rootVisualElement.Add(toolbar);

            graph = new SkillEditorGraph(this,searchWindowProvider);
            rootVisualElement.Add(graph);
        }

        public void SetData(ref SkillContentData v)
        {
            this.data = v;
            this.Refresh();
        }

        public void Refresh()
        {
            skillSpaceIds = new string[data.spaces.Length];
            for (int i = 0; i < data.spaces.Length; i++)
            {
                skillSpaceIds[i] = i + "." + data.spaces[i].id;
            }

            for (int i = 0; i < data.spaces.Length; i++)
            {
                var skillSpace = data.spaces[i];

                foreach (var skillAction in skillSpace.actions.Values)
                {
                    foreach (var item in skillAction.dataKey)
                    {
                        if (data.dataBool.ContainsKey(item.Value) && !skillSpace.dataBool.ContainsKey(item.Value))
                        {
                            skillSpace.dataBool.Add(item.Value, data.dataBool[item.Value]);
                        }
                        else if (data.dataEdge.ContainsKey(item.Value) && !skillSpace.dataEdge.ContainsKey(item.Value))
                        {
                            skillSpace.dataEdge.Add(item.Value, data.dataEdge[item.Value]);
                        }
                        else if (data.dataInt.ContainsKey(item.Value) && !skillSpace.dataInt.ContainsKey(item.Value))
                        {
                            skillSpace.dataInt.Add(item.Value, data.dataInt[item.Value]);
                        }
                        else if (data.dataString.ContainsKey(item.Value) && !skillSpace.dataString.ContainsKey(item.Value))
                        {
                            skillSpace.dataString.Add(item.Value, data.dataString[item.Value]);
                        }
                    }
                }
            }

            if (graph != null)
            {
                graph.Clear();
                rootVisualElement.Remove(graph);
            }

            graph = new SkillEditorGraph(this,searchWindowProvider);
            rootVisualElement.Add(graph);
            subWindow.BringToFront();

            foreach (SkillNode item in space.actions.Values)
            {
                AddNode(item);
            }

            if(space.actions.Count == 0)
            {
                Node nodeBegin = CreateNewNode("Begin");
                nodeBegin.SetPosition(new Rect(600, 200, 0, 0));
            }

            foreach (SkillNode node in space.actions.Values)
            {
                foreach (var dataKey in node.dataKey)
                {
                    SkillEditorGraphNode inNode = GetNode(node.id);
                    Port inPort = inNode.GetPort(dataKey.Key);
                    if (inPort == null)
                        continue;

                    if (inPort.portType == typeof(StructStep))
                    {
                        SkillEditorGraphNode outNode = GetNode(dataKey.Value);
                        Port outPort = outNode.GetPort("Previous");
                        graph.AddElement(outPort.ConnectTo(inPort));
                    }
                    else if (data.dataEdge.ContainsKey(dataKey.Value))
                    {
                        if (data.dataEdge[dataKey.Value] == node.id)
                            continue;

                        SkillEditorGraphNode outNode = GetNode(space.dataEdge[dataKey.Value]);

                        foreach (var outDatakey in outNode.data.dataKey)
                        {
                            if (outDatakey.Value == dataKey.Value)
                            {
                                Port outPort = outNode.GetPort(outDatakey.Key);

                                if (outPort != null)
                                {
                                    Debug.Log(string.Format("{0} connect {1} | {2}", inNode.info.id, outNode.info.id, outDatakey.Key));

                                    graph.AddElement(outPort.ConnectTo(inPort));
                                }

                                break;
                            }
                        }
                    }
                }
            }
        }

        public SkillEditorGraphNode GetNode(string id)
        {
            var list = graph.graphElements.ToList();
            foreach (var item in list)
            {
                if (item is SkillEditorGraphNode node && node.data.id == id)
                {
                    return node;
                }
            }

            Debug.LogError("not find node " + id);

            return null;
        }

        public static string GuidTo16String()
        {
            long i = 1;
            foreach (byte b in Guid.NewGuid().ToByteArray())
                i *= ((int)b + 1);
            return string.Format("{0:x}", i - DateTime.Now.Ticks);
        }

        public Node CreateNewNode(string type)
        {
            SkillNode skillNode = new SkillNode();
            skillNode.id = GuidTo16String();
            skillNode.type = type;
            Node node = AddNode(skillNode);
            return node;
        }

        public Node AddNode(SkillNode skillNode)
        {
            if (dicNodeInfo.ContainsKey(skillNode.type) == false)
                Debug.LogError("not find node type " + skillNode.type);

            SkillEditorNodeInfo nodeInfo = dicNodeInfo[skillNode.type];
            SkillEditorGraphNode node = new SkillEditorGraphNode();
            node.SetInfo(nodeInfo);
            node.SetData(space, skillNode);

            graph.AddElement(node);

            return node;
        }

        private void FileSave()
        {
            var list = graph.graphElements.ToList();

            SkillSpace space = data.spaces[skillSpaceIndex];
            space.dataBool.Clear();
            space.dataInt.Clear();
            space.dataString.Clear();
            space.dataEdge.Clear();
            space.actions.Clear();
            space.beginAction = "";

            for (int i = 0; i < list.Count; i++)
            {
                if (list[i] is SkillEditorGraphNode node)
                {
                    SkillNode skillNode = node.GetDataAndUpdate();
                    space.actions.Add(skillNode.id, skillNode);

                    if (skillNode.type == "Begin")
                    {
                        space.beginAction = skillNode.id;
                    }
                }
            }

            for (int i = 0; i < list.Count; i++)
            {
                if (list[i] is Edge edge)
                {
                    var inputNode = edge.input.node as SkillEditorGraphNode;
                    var outputNode = edge.output.node as SkillEditorGraphNode;

                    if (edge.input.portType == typeof(StructStep))
                    {
                        outputNode.data.dataKey[edge.output.portName] = inputNode.data.id;
                    }
                    else
                    {
                        inputNode.data.dataKey[edge.input.portName] = outputNode.data.dataKey[edge.output.portName];
                        space.dataEdge[outputNode.data.dataKey[edge.output.portName]] = outputNode.data.id;
                    }
                }
            }

            data.dataInt.Clear();
            data.dataBool.Clear();
            data.dataEdge.Clear();
            data.dataString.Clear();

            for (int i = 0; i < data.spaces.Length; i++)
            {
                var skillSpace = data.spaces[i];

                foreach (var item in skillSpace.dataInt)
                {
                    data.dataInt.Add(item.Key, item.Value);
                }
                foreach (var item in skillSpace.dataBool)
                {
                    data.dataBool.Add(item.Key, item.Value);
                }
                foreach (var item in skillSpace.dataEdge)
                {
                    data.dataEdge.Add(item.Key, item.Value);
                }
                foreach (var item in skillSpace.dataString)
                {
                    data.dataString.Add(item.Key, item.Value);
                }
            }
        }

        private void OnDestroy()
        {
            //FileSave();
        }

        private void ChangeDataHandler(ChangeDataEvent evt)
        {
            if (evt.data == null)
            {
                subWindow.visible = false;
            }
            else
            {
                subWindow.SetText(evt.data.GetDesc());
                subWindow.visible = true;
            }
        }

        public static Dictionary<int, string> dicTriggers = new Dictionary<int, string>() {
            {1,"人物初始化" },
            {2,"战斗准备时" },
            {3,"战斗开始" },
            {4,"血量为0时" },
            {5,"死亡时" },
            {6,"受到控制时" },
            {7,"更换目标时" },
            {8,"满蓝时" },
            {9,"击杀时" },
            {10,"检测释放" },
            {11,"位置变动时" },
            {12,"落地站起时" },

            {101,"对目标造成伤害计算前" },
             {102,"对目标造成伤害前" },
              {103,"对目标伤害扣血前" },
              {104,"对目标造成伤害后" },

              {111,"对目标造成治疗计算前" },
             {112,"对目标造成治疗前" },
              {113,"对目标加血前" },
              {114,"对目标造成治疗后" },

              {201,"受到伤害计算前" },
               {202,"受到伤害前" },
              {203,"受到伤害扣血前" },
              {204,"受到伤害后" },

              {211,"受到治疗计算前" },
               {212,"受到治疗前" },
              {213,"受到治疗加血前" },
              {214,"受到治疗后" },

               {301,"技能释放时" },
               {302,"技能开始时" },
                {303,"技能引导触发时" },
                 {304,"技能结束时" },
                 {305,"技能打断时" },

                  {401,"Buff时间" },
                  {402,"Buff添加时" },
                  {403,"Buff移除时" },
                  {404,"Buff叠满时" },
                  {405,"Buff触发时" },

                   {501,"护盾创建时" },
                   {502,"护盾添加时" },
                    {503,"护盾移除时" },
                     {504,"护盾消耗完时" },
        };
    }

    public class ChangeDataEvent : EventBase<ChangeDataEvent>
    {
        public SkillEditorGraphNode data;

        public ChangeDataEvent()
        {
            bubbles = true;
        }
    }
}